#include <illib/Hash.h>
#include "String.h"

namespace ilrt{
	String::~String(){

	}
	String::String()
	{

	}

	char * String::string(){
		return start();
	}

	namespace StringCache{
		Entry::Entry() :
			String()
		{
		}
		void Entry::setup(char * string, size_t length, uint32_t hash) {
			String::setup(string, length);
			hash_ = hash;
		}
		Entry::~Entry(){
		}
		StringTree::StringTree(){

		}
		bool StringTreeFunctions::lesser(StringTree::NodeType &left, StringTree::NodeType &right){
			bool result = (left.hash_ < right.hash_);
			if (left.hash_ == right.hash_) {
				result = strcmp(left.string(), right.string()) < 0;
			}
			return result;
		}
		bool StringTreeFunctions::greater(StringTree::NodeType &left, StringTree::NodeType &right){
			bool result = (left.hash_ > right.hash_);
			if (left.hash_ == right.hash_) {
				result = strcmp(left.string(), right.string()) > 0;
			}
			return result;
		}
		bool StringTreeFunctions::equal(StringTree::NodeType &left, StringTree::NodeType &right){
			bool result = (&left == &right);
			if (!result && (left.hash_ == right.hash_)) {
				result = strcmp(left.string(), right.string()) == 0;
			}
			return result;
		}
		Cache::Cache()
		{
		}
		void Cache::clear(){
			StringTreeFunctions functions;
			StringTree::IteratorType i(stringTree_, functions);
			while(i.current()) {
				StringTree::NodeType * e = i.current();
				i.next();
				stringTree_.remove(*e);
				e->destroy();
			}
		}
		String * Cache::get(char * string){
			return this->get(string, strlen(string));
		}
		String * Cache::get(String & string){
			return this->get(string.string(), string.length());
		}
		String * Cache::get(char * string, size_t length){
			uint32_t hash = illib::Hash::Buffer(illib::Hash::FNV, string, length, illib::Hash::FNVInit);
			StringTree::NodeType requested;
			requested.setup(string, length, hash);
			StringTreeFunctions functions;
			StringTree::IteratorType i(requested, stringTree_, functions);
			if(i.current()) {
				Entry * cachedString = i.current();
				return cachedString;
			}
			return 0;
		}
		String & Cache::add(illib::Allocator &allocator, char * string) {
			return add(allocator, string, strlen(string));
		}
		String & Cache::add(illib::Allocator &allocator, char * string, size_t length) {
			uint32_t hash = illib::Hash::Buffer(illib::Hash::FNV, string, length, illib::Hash::FNVInit);
			StringTree::NodeType & entry = StringTree::NodeType::create(allocator, sizeof(Entry));
			entry.setup(string, length, hash);
			StringTreeFunctions functions;
			stringTree_.add(entry, functions);
			return entry;
		}
		String & Cache::add(illib::Allocator &allocator, String& string) {
			return add(allocator, string.string(), string.length());
		}
		String & Cache::string(illib::Allocator &allocator, String& str) {
			return string(allocator, str.string(), str.length());
		}
		String & Cache::string(illib::Allocator &allocator, char * str){
			return string(allocator, str, strlen(str));
		}
		String & Cache::string(illib::Allocator &allocator, char * str, size_t length){
			uint32_t hash = illib::Hash::Buffer(illib::Hash::FNV, str, length, illib::Hash::FNVInit);
			StringTree::NodeType requested;
			requested.setup(str, length, hash);
			StringTreeFunctions functions;
			StringTree::IteratorType i(requested, stringTree_, functions);
			if(i.current()) {
				Entry * cachedString = i.current();
				return *cachedString;
			}
			StringTree::NodeType & entry = StringTree::NodeType::create(allocator, sizeof(StringTree::NodeType));
			entry.setup(str, length, hash);
			stringTree_.add(entry, functions);
			return entry;
		}
	};
	StringType::~StringType(){

	}
	String & StringType::intern(String & string){
		return cache_.string(cacheAllocator_, string);
	}
	String & StringType::intern(char * string){
		return cache_.string(cacheAllocator_, string);
	}
	String & StringType::intern(char * string, size_t length){
		return cache_.string(cacheAllocator_, string, length);
	}


}
